package com.gitee.wsl.ext.bitcode

import kotlin.jvm.JvmName

// Default size of one element in the array used to store bits.
private const val ELEMENT_SIZE: Int = 64
private const val MAX_BIT_OFFSET: Int = ELEMENT_SIZE - 1

private const val ALL_TRUE: Long = -1L // 0xFFFF_FFFF_FFFF_FFFF
private const val ALL_FALSE: Long = 0L // 0x0000_0000_0000_0000

@JvmName("maskBits")
fun mask(bits: Int) = if (bits >= Int.SIZE_BYTES * 8) 0.inv() else (1 shl bits) - 1

/** Creates an [Int] with [this] bits set to 1 */
fun Int.mask(): Int = (1 shl this) - 1
/** Creates a [Long] with [this] bits set to 1 */
fun Long.mask(): Long = (1L shl this.toInt()) - 1L

/** Creates an [Int] with [this] bits set to 1, displaced [offset] bits */
fun Int.mask(offset: Int): Int = mask() shl offset
/** Creates a [Long] with [this] bits set to 1, displaced [offset] bits */
fun Long.mask(offset: Int): Long = mask() shl offset


// Transforms a bit offset to the mask with only bits before the index (inclusive) set.
 val Int.asMaskBefore: Long
    get() = getMaskBetween(0, this)

// Transforms a bit offset to the mask with only bits after the index (inclusive) set.
 val Int.asMaskAfter: Long
    get() = getMaskBetween(this, MAX_BIT_OFFSET)

// Transforms a bit offset to the mask with only bit set corresponding to the offset.
internal val Int.asMask: Long
    get() = 0x1L shl this

// Builds a masks with 1 between fromOffset and toOffset (both inclusive).
internal fun getMaskBetween(fromOffset: Int, toOffset: Int): Long {
    var res = 0L
    val maskToAdd = fromOffset.asMask

    for (i in fromOffset..toOffset) {
        res = (res shl 1) or maskToAdd
    }

    return res
}

// Transforms a bit index into an element index in the `bits` array.
internal val Int.elementIndex: Int
    get() = this / ELEMENT_SIZE

// Transforms a bit index in the set into a bit in the element of the `bits` array.
internal val Int.bitOffset: Int
    get() = this % ELEMENT_SIZE

// Transforms a bit index in the set into pair of a `bits` element index and a bit index in the element.
internal val Int.asBitCoordinates: Pair<Int, Int>
    get() = Pair(elementIndex, bitOffset)